package com.zzw.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;

/**
 * 阿里云OSS工具类，用于与阿里云对象存储服务进行交互。
 * 提供文件上传、删除等基本操作。
 *
 * @author 赵志伟
 * @version 1.0
 */
@Component
public class OssUtil {

    private static Logger log = LoggerFactory.getLogger(OssUtil.class);

    // 静态变量用于全局访问
    public static String OSS_ENDPOINT;
    public static String OSS_BUCKET_NAME;
    public static String OSS_ACCESS_KEY_ID;
    public static String OSS_ACCESS_KEY_SECRET;
    public static String OSS_URL_PREFIX;

    // 实例变量用于接收配置
    @Value("${oss.endpoint}")
    private String endpoint;

    @Value("${oss.bucketName}")
    private String bucketName;

    @Value("${oss.accessKeyId}")
    private String accessKeyId;

    @Value("${oss.accessKeySecret}")
    private String accessKeySecret;

    @Value("${oss.urlPrefix}")
    private String urlPrefix;

    /**
     * 初始化方法，将配置值从实例变量赋给静态变量
     */
    @PostConstruct
    public void init() {
        OSS_ENDPOINT = this.endpoint;
        OSS_BUCKET_NAME = this.bucketName;
        OSS_ACCESS_KEY_ID = this.accessKeyId;
        OSS_ACCESS_KEY_SECRET = this.accessKeySecret;
        // 构建URL前缀，用于生成访问路径
        OSS_URL_PREFIX = "https://" + OSS_BUCKET_NAME + "." + OSS_ENDPOINT;

        log.info("OSS配置初始化完成，endPoint={}, bucketName={}, urlPrefix={}",
                OSS_ENDPOINT, OSS_BUCKET_NAME, OSS_URL_PREFIX);

        // 初始化时确保Bucket存在且已设置正确的权限
        ensureBucketExists();
    }

    /**
     * 确保Bucket存在且设置了正确的访问权限
     */
    public static boolean ensureBucketExists() {
        OSS ossClient = null;
        try {
            ossClient = new OSSClientBuilder().build(OSS_ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);

            // 如果Bucket不存在则创建
            if (!ossClient.doesBucketExist(OSS_BUCKET_NAME)) {
                ossClient.createBucket(OSS_BUCKET_NAME);
                log.info("创建Bucket成功: {}", OSS_BUCKET_NAME);
            }

            // 不管Bucket是否存在，都设置Bucket为公共读权限
            try {
                ossClient.setBucketAcl(OSS_BUCKET_NAME, CannedAccessControlList.PublicRead);
                log.info("成功设置Bucket公共读权限: {}", OSS_BUCKET_NAME);
            } catch (Exception e) {
                log.error("设置Bucket公共读权限失败: {}", e.getMessage());
                // 继续执行，不要因为权限设置失败而中断流程
            }
            return true;
        } catch (Exception e) {
            log.error("确保Bucket存在失败: {}", e.getMessage());
            return false;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 上传文件到OSS
     *
     * @param file 要上传的文件
     * @param objectName 对象名称（存储路径）
     * @return 文件访问URL
     */
    public static String upload(File file, String objectName) {
        OSS ossClient = null;
        FileInputStream fileInputStream = null;

        try {
            // 确保对象名称不以斜杠开头
            if (objectName != null && objectName.startsWith("/")) {
                objectName = objectName.substring(1);
                log.warn("对象名称以'/'开头，已自动删除前导斜杠: {}", objectName);
            }

            log.info("开始上传文件到OSS，对象名称: {}, 文件大小: {}KB",
                    objectName, file.length() / 1024);
            fileInputStream = new FileInputStream(file);

            // 创建OSS客户端
            ossClient = new OSSClientBuilder().build(OSS_ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);

            // 创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(file.length());

            // 设置Content-Type
            String contentType = detectContentType(file);
            metadata.setContentType(contentType);
            log.debug("文件Content-Type: {}", contentType);

            // 上传文件
            ossClient.putObject(OSS_BUCKET_NAME, objectName, fileInputStream, metadata);

            // 生成文件URL
            String url = OSS_URL_PREFIX + "/" + objectName;
            log.info("文件上传成功，OSS URL => {}", url);

            return url;
        } catch (Exception e) {
            log.error("上传文件到OSS失败: {}, 错误类型: {}, 详细信息: {}",
                    e.getMessage(), e.getClass().getName(), e);
            throw new RuntimeException("上传文件到OSS失败", e);
        } finally {
            // 关闭资源
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (Exception e) {
                log.error("关闭文件流失败", e);
            }

            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 上传数据流到OSS
     *
     * @param inputStream 输入流
     * @param objectName 对象名称（存储路径）
     * @param contentType 内容类型
     * @return 文件访问URL
     */
    public static String uploadStream(InputStream inputStream, String objectName, String contentType) {
        OSS ossClient = null;

        try {
            // 确保对象名称不以斜杠开头
            if (objectName != null && objectName.startsWith("/")) {
                objectName = objectName.substring(1);
                //log.warn("对象名称以'/'开头，已自动删除前导斜杠: {}", objectName);
            }
            // 创建OSS客户端
            ossClient = new OSSClientBuilder().build(OSS_ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);

            // 创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            if (contentType != null && !contentType.isEmpty()) {
                metadata.setContentType(contentType);
            }

            // 上传文件
            ossClient.putObject(OSS_BUCKET_NAME, objectName, inputStream, metadata);

            // 生成文件URL
            String url = OSS_URL_PREFIX + "/" + objectName;
            log.info("文件流上传成功，OSS URL => {}", url);

            return url;
        } catch (Exception e) {
            log.error("上传文件流到OSS失败: {}, 错误类型: {}", e.getMessage(), e.getClass().getName());
            throw new RuntimeException("上传文件流到OSS失败", e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 删除OSS中的文件
     *
     * @param objectName 对象名称（存储路径）
     * @return 是否删除成功
     */
    public static boolean delete(String objectName) {
        OSS ossClient = null;


        try {
            // 确保对象名称不以斜杠开头
            if (objectName != null && objectName.startsWith("/")) {
                objectName = objectName.substring(1);
                log.warn("对象名称以'/'开头，已自动删除前导斜杠: {}", objectName);
            }

            log.info("准备删除OSS文件，对象名称: {}", objectName);
            // 创建OSS客户端
            ossClient = new OSSClientBuilder().build(OSS_ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);

            // 删除文件
            ossClient.deleteObject(OSS_BUCKET_NAME, objectName);
            log.info("删除文件成功: {}", objectName);

            return true;
        } catch (Exception e) {
            log.error("删除文件失败: {}, 错误类型: {}", e.getMessage(), e.getClass().getName());
            return false;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 获取文件的临时访问URL（带签名的URL，用于私有Bucket）
     *
     * @param objectName 对象名称
     * @param expiration 过期时间
     * @return 临时访问URL
     */
    public static String generatePresignedUrl(String objectName, Date expiration) {
        OSS ossClient = null;

        try {
            // 创建OSS客户端
            ossClient = new OSSClientBuilder().build(OSS_ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);

            // 生成签名URL
            URL url = ossClient.generatePresignedUrl(OSS_BUCKET_NAME, objectName, expiration);
            return url.toString();
        } catch (Exception e) {
            log.error("生成签名URL失败: {}", e.getMessage());
            throw new RuntimeException("生成签名URL失败", e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 检测文件的Content-Type
     *
     * @param file 文件
     * @return 内容类型
     */
    private static String detectContentType(File file) {
        String fileName = file.getName();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();

        switch (suffix) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "bmp":
                return "image/bmp";
            case "webp":
                return "image/webp";
            case "pdf":
                return "application/pdf";
            case "doc":
                return "application/msword";
            case "docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xls":
                return "application/vnd.ms-excel";
            case "xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "ppt":
                return "application/vnd.ms-powerpoint";
            case "pptx":
                return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
            case "txt":
                return "text/plain";
            case "html":
            case "htm":
                return "text/html";
            case "css":
                return "text/css";
            case "js":
                return "application/javascript";
            case "json":
                return "application/json";
            case "xml":
                return "application/xml";
            case "zip":
                return "application/zip";
            case "rar":
                return "application/x-rar-compressed";
            case "7z":
                return "application/x-7z-compressed";
            case "mp3":
                return "audio/mpeg";
            case "mp4":
                return "video/mp4";
            case "avi":
                return "video/x-msvideo";
            case "mov":
                return "video/quicktime";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 直接上传MultipartFile到OSS
     *
     * @param file MultipartFile对象
     * @param objectName 对象名称（存储路径）
     * @return 文件访问URL
     */
    public static String uploadMultipartFile(MultipartFile file, String objectName) {
        OSS ossClient = null;
        InputStream inputStream = null;

        try {
            // 确保对象名称不以斜杠开头
            if (objectName != null && objectName.startsWith("/")) {
                objectName = objectName.substring(1);
                log.warn("对象名称以'/'开头，已自动删除前导斜杠: {}", objectName);
            }

            log.info("开始上传MultipartFile到OSS，对象名称: {}, 文件大小: {}KB",
                    objectName, file.getSize() / 1024);
            inputStream = file.getInputStream();

            // 创建OSS客户端
            ossClient = new OSSClientBuilder().build(OSS_ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);

            // 创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(file.getSize());

            // 设置Content-Type
            String contentType = file.getContentType();
            if (contentType == null || contentType.isEmpty()) {
                contentType = "application/octet-stream";
            }
            metadata.setContentType(contentType);
            log.debug("文件Content-Type: {}", contentType);

            // 上传文件
            ossClient.putObject(OSS_BUCKET_NAME, objectName, inputStream, metadata);

            // 生成文件URL
            String url = OSS_URL_PREFIX + "/" + objectName;
            log.info("文件上传成功，OSS URL => {}", url);

            return url;
        } catch (Exception e) {
            log.error("上传MultipartFile到OSS失败: {}, 错误类型: {}", e.getMessage(), e.getClass().getName());
            throw new RuntimeException("上传MultipartFile到OSS失败", e);
        } finally {
            // 关闭资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                log.error("关闭输入流失败", e);
            }

            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 格式化OSS路径
     */
    public static String formatOssPath(String path) {
        if (path == null) return "";

        // 移除前导斜杠
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        // 确保路径以斜杠结尾
        if (!path.endsWith("/")) {
            path += "/";
        }

        return path;
    }
}
